/*
Dominique Jannone
Guillaume Marre
Yohan Ojeda
Laurent Schiavon
*/
package model;

import java.net.*;
import java.io.*;
import java.sql.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.nio.channels.*;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class MainServer implements serviceInt {
   
    final static int NB_THREADS = 20;
    final static int PORT_BASE_DEFAUT = 3000;
    final static String FICHIER_CONFIGURATION_LOG = "/LOG_serveur.properties";
    static int userServiceCpt;
    static int annonceurServiceCpt;

    static private String agentId="1234";
    static private String agentVersion="0.0";
    static private String agentMS="Master";
    

    public static final Logger logger = Logger.getLogger(MainServer.class.getName());
    private static ExecutorService pool;
    public static boolean serverOn;

    public static CanalUserHandler canalUser;
    public CanalAdminHandler canalAdmin;
    public CanalAnnonceurHandler canalAnnonceur;

    private MonitorConnexion monitor = new MonitorConnexion();
    public static int debitCanalUser;
    static int portBase;

    int getDebitCanalUser(){
	return debitCanalUser;
    }

    public static boolean isServerOn(){
	return serverOn;
    }

    void halt(){
	pool.shutdown();
	try{
	    pool.awaitTermination(60,TimeUnit.SECONDS);
	}catch(InterruptedException e){}
	canalUser.interrupt();
	canalAnnonceur.interrupt();
	serverOn = false;
    }

    void interruptAdmin(){
	canalAdmin.interrupt();
    }

    private MainServer(int poolsize, int portbase) throws Exception {
    
	serverOn = true;
	pool = Executors.newFixedThreadPool(poolsize);
	canalAnnonceur = new CanalAnnonceurHandler(portbase+1, pool);
	canalUser = new CanalUserHandler(portbase, pool, MainServer.this, canalAnnonceur);
	canalAdmin  = new CanalAdminHandler(portbase+2, pool);
	
//	monitor.start();
	canalUser.start();
	canalAnnonceur.start();
	canalAdmin.start();
	
    }

    static String getMasterOrSlave(){
	return agentMS;
    }

    static String getId(){
	return agentId;
    }

    static String getVersion(){
	return agentVersion;
    }

 // Setter
    public static int IncrePortBase() {
    		portBase = portBase+1;
    		return portBase;}

    /**
     * Describe <code>main</code> method here.
     *
     * @param args a <code>String</code> value
     */
    public static void main(final String[] args) {
      	
		// Chargement du fichier de configuration des logs

	final InputStream inputStream = MainServer.class.getResourceAsStream(FICHIER_CONFIGURATION_LOG);
		try
		{
	    LogManager.getLogManager().readConfiguration(inputStream);
		}
		catch (final IOException e)
		{
		    Logger.getAnonymousLogger().severe("Le fichier de configuration de log: " +FICHIER_CONFIGURATION_LOG +" n'a pas pu être chargé");
		    Logger.getAnonymousLogger().severe(e.getMessage());
		 	
		}
		
	int portBase = PORT_BASE_DEFAUT;;
	if (args.length > 0)
	    try{
		portBase = Integer.parseInt(args[0]);
	    }catch(Exception e){}

	Object sync = new Object();
	try{
	    MainServer mainServer = new MainServer(NB_THREADS, portBase);
	    mainServer.canalUser.join();
	    logger.log(Level.INFO,"Diffuser user server halted");
	    mainServer.monitor.terminate();
	    mainServer.canalAnnonceur.join();
	    logger.log(Level.INFO,"Diffuser annoncer server halted");
	    mainServer.canalAdmin.join();
	    
        } catch (Exception e) {
            logger.log(Level.INFO,"Diffuser: " + e.toString());
            logger.log(Level.SEVERE,e.getClass() + ": " +  e.getMessage(), e);
        } finally {
        }
	logger.log(Level.INFO,"Diffuser server halted\n");
    }


	
    
	@Override
	public boolean EnvoiObject2DFromServerToClient(UserServerDelegate usd,
			Object[][] value, ObjectOutputStream oos) {
		 // Creation du flux de sortie
		  try {
			  logger.log(Level.INFO," ---> Envoi des infos en cours au serveur sur le port " + PORT_BASE_DEFAUT +"...");
			  logger.log(Level.INFO,"NB de ligne: "+value.length);
			  logger.log(Level.INFO,"NB de colonne: "+value[0].length);
			  
			  //Envoi du tableau vers le serveur
			  for (int i = 0; i < value.length; i++) {
				  for (int j = 0; j < value[i].length; j++) {
				    Object element = value[i][j];
				    logger.log(Level.INFO,"Les données a envoyees sont : " + element +"\n");						       
					oos.writeObject(element);
					oos.flush();
				}
			  }			   
		    } catch (Exception e2) {
		    	logger.log(Level.SEVERE," ---> Erreur en creant le flux de sortie : " + e2);
		    	logger.log(Level.SEVERE,e2.getClass() + ": " +  e2.getMessage(), e2);
		    	return false;
		    }
	return true;
	}
	
//	public boolean EnvoiUpdateTabServerToClient(UserServerDelegate usd,
//			Object[][] value, ObjectOutputStream oos) {
//		
//		 // Creation du flux de sortie
//		  try {
//			  		oos.reset();
//	 				//Envoie de la taille du tableau		
//				  		Object elementID = 0;					       
//						oos.writeObject(elementID);
//						//Envoie de la description
//						Object elementTaille = value.length;						       
//						oos.writeObject(elementTaille);
//						oos.flush();
//			  
//			  
//		      System.out.println(" ---> Envoi des infos en cours au serveur sur le port " + PORT_BASE_DEFAUT +"...");
//			  System.out.println("NB de ligne: "+value.length);
//			  System.out.println("NB de colonne: "+value[0].length);
//			  
//			  //Envoi du tableau vers le serveur
//			  for (int i = 0; i < value.length; i++) {
//				  for (int j = 0; j < value[i].length; j++) {
//				    Object element = value[i][j];
//				    System.out.print("Les données a envoyees sont : " + element +"\n");						       
//					oos.writeObject(element);
//					oos.flush();
//				}
//			  }			   
//		    } catch (Exception e2) {
//		    	System.out.println(" ---> Erreur en creant le flux de sortie : " + e2);
//		    	return false;
//		    }
//	return true;
//	}
	


	@Override
	public boolean EnvoiObjectFromServerToClient(UserServerDelegate usd,
			Object[] value, ObjectOutputStream oos) {
		// Creation du flux de sortie
		  try {
				                                    
				//Envoi du tableau vers le serveur
	            for (int i = 0; i < value.length; i++) {            	
	            	Object element = value[i];
	            	logger.log(Level.INFO,"Les données a envoyees sont : " + element);                                                      
	            	oos.writeObject(element);
	            }
	                                                                     
		  }catch (Exception e2) {
			  logger.log(Level.SEVERE," ---> Erreur en creant le flux de sortie : " + e2);
			  logger.log(Level.SEVERE,e2.getClass() + ": " +  e2.getMessage(), e2);
			  return false;
		  }
	return true;
	}


	@Override
	public Object[][] ReceptionObjet2DFromClient(UserServerDelegate usd,
			int tailleLigne, int tailleColonne, ObjectInputStream ois) {
		 //Déclaration des variables
	      Object[][] value = new Object[2][2];
	      
	       try {
	      	 //Création de la socket
	        // Serveur = new ServerSocket(port);
	    	   logger.log(Level.INFO,"port pour recevoir les données du client : " + PORT_BASE_DEFAUT);
	         	      
	         //Parcours (lecture) du tableau reçu
		         for (int i = 0; i < 3; i++) {
		      	   for (int j = 0; j < 3; j++) {
		      	   value = (Object[][]) ois.readObject();
		      	   value[i][j] = value;
		      	 logger.log(Level.INFO,"Données dans le tableau :" + value);
		      	   }
		         }
	       }catch (Exception e) {
	    	   logger.log(Level.SEVERE," ---> Erreur en creant le flux de sortie : " + e);
	    	   logger.log(Level.SEVERE,e.getClass() + ": " +  e.getMessage(), e);}
	      return value;
	}


	@Override
	public Object[] ReceptionObjetFromClient(UserServerDelegate usd,
			int tailleLigne,ObjectInputStream ois) {
		 //Déclaration des variables
	      Object[] valueRet = new Object[tailleLigne];
	     
	       try {
	      	 //Création de la socket
	    	   logger.log(Level.INFO,"port pour recevoir les données du client : " + PORT_BASE_DEFAUT);
	         

	         //Parcours (lecture) du tableau reçu
	         for (int i = 0; i < tailleLigne; i++) {
	      	   Object valueRead = ois.readObject();
	      	   valueRet[i] = valueRead;
	      	 logger.log(Level.INFO,"Données dans le tableau :" + valueRead);
	      	}        
	       }catch (Exception e) {
	       logger.log(Level.SEVERE," ---> Erreur en creant le flux de input pour reception le tableau simple : " + e);
	       logger.log(Level.SEVERE,e.getClass() + ": " +  e.getMessage(), e);}
	       return valueRet;
	}
	
	@Override
	public Object[] ReceptionACKFromClient(UserServerDelegate usd, ObjectInputStream ois) {
		Object[] valueRepTab = new Object[2];
			 try {			   
			   // TMP
				 InputStream input = UserServerDelegate.socket.getInputStream();
				 ObjectInputStream ois2 = new ObjectInputStream(input);
				 logger.log(Level.INFO,">>>>>>>>>>>>>>ois2.available(): "+ois2.available());
				 
			   //Parcours (lecture) du tableau reçu
			   for (int i = 0; i < 2; i++) {
				   Object valueReponse = ois2.readObject();
				   valueRepTab[i] = valueReponse;
				   logger.log(Level.INFO,"Réponse du Client: "+valueReponse.toString());
				}
			   return valueRepTab;
			 }catch (Exception e) {
			 logger.log(Level.SEVERE," ---> Erreur en creant le flux de input : " + e.getStackTrace());
			 valueRepTab[0] = null;
			 valueRepTab[1] = null;
			 return valueRepTab;
			 }
	}
	
	@Override
	public boolean EnvoiReponseACK(String id , String description, boolean resultat, ObjectOutputStream oos) {
		  try {
	   		  // Définition de l'id et de la description
	   			  if (resultat){
	   				//Envoie de l'ID		
	 			  		Object elementID = id;					       
	 					oos.writeObject(elementID);
	 					//Envoie de la description
	 					Object elementDescitpion = description;						       
	 					oos.writeObject(elementDescitpion);
		    		  }
		    		  else{
		    			//Envoie de l'ID		
	 			  		Object elementID = 1;					       
	 					oos.writeObject(elementID);
	 					//Envoie de la description
	 					Object elementDescitpion = description;						       
	 					oos.writeObject(elementDescitpion);
		    		  }	    
				  	oos.flush();
	   		    } catch (Exception e2) {
	   		    	logger.log(Level.SEVERE," ---> Erreur en creant le flux de sortie : " + e2);
	   	      return false;
	   		    }													
			return true;
		}
}